package org.github.p2w.util;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.github.p2w.constant.Constant;
import org.github.p2w.entity.*;

import java.util.*;
import java.util.List;
import java.util.regex.Matcher;

/**
 * postman转word
 *
 * @author xiayongchao
 * @date 2022/08/02
 */
@Slf4j
public class Postman2Word extends Word {

    private final Postman postman;

    private final Map<String, String> envMap = new HashMap<>();

    public Postman2Word(Postman postman, JSONObject environment) {
        super();
        this.postman = postman;
        if (environment != null && !environment.isEmpty()) {
            JSONArray jsonArray = environment.getJSONArray("values");
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                // 如果key有重复的，后面的key会覆盖前面的
                // {{key}}
                String key = Constant.ENV_START + jsonObject.getStr("key") + Constant.ENV_END;
                this.envMap.put(key, jsonObject.getStr("value"));
            }
        }
    }

    /**
     * 添加tag的接口列表
     */
    private final List<String> addList = new ArrayList<>(8);

    /**
     * 更新tag的接口列表
     */
    private final List<String> updateList = new ArrayList<>(8);

    /**
     * 删除tag的接口列表
     */
    private final List<String> deleteList = new ArrayList<>(8);

    /**
     * 转化word文件
     *
     * @return {@link Postman2Word}
     */
    public Postman2Word p2w() {
        log.info("----------开始生成接口文档----------");
        Config config = Config.getInstance();
        // 创建文档标题
        createTitle(postman.getFileName());
        createBreak();
        // 创建目录预览
        List<String> apiNames = getApiNames();
        apiNames.forEach(apiName -> createText(apiName, "", StrUtil.count(apiName, Constant.NAME_INDEX_FLAG)));
        createBreak();
        List<Item> itemList = getItems();
        itemList.forEach(item -> matchTag(config, item.getName()));
        // 创建标签目录
        Map<String, String> tableColorMap = new HashMap<>(addList.size() + updateList.size() + deleteList.size());
        if (config.isTagMatch()) {
            if (!addList.isEmpty()) {
                createText(config.getAddName(), "00FF00");
                for (String name : addList) {
                    createText(name, "00FF00");
                    tableColorMap.put(name, "00FF00");
                }
                createBreak();
            }
            if (!updateList.isEmpty()) {
                createText(config.getUpdateName(), "0000FF");
                for (String name : updateList) {
                    createText(name, "0000FF");
                    tableColorMap.put(name, "0000FF");
                }
                createBreak();
            }
            if (!deleteList.isEmpty()) {
                createText(config.getDeleteName(), "FF0000");
                for (String name : deleteList) {
                    createText(name, "FF0000");
                    tableColorMap.put(name, "FF0000");
                }
                createBreak();
            }
        }
        // 创建表格
        for (int i = 0; i < itemList.size(); i++) {
            Item item = itemList.get(i);
            Map<String, String> data = new LinkedHashMap<>();
            Request request = item.getRequest();
            if (request == null) {
                continue;
            }
            String method = request.getMethod();
            data.put("接口名称", item.getName());
            data.put("接口地址", matchEnvironment(request.getUrl().getRequestAddress()));
            data.put("请求方式", method);
            data.put("Headers", request.getHeaderParams());
            String params;
            if (Constant.GET.equals(method)) {
                params = request.getUrl().getRequestParams();
            } else {
                Body body = request.getBody();
                if (body != null) {
                    params = body.getRaw();
                } else {
                    params = request.getUrl().getRequestParams();
                }
            }
            data.put("参数", matchEnvironment(params));
            if (item.getResponse() != null && !item.getResponse().isEmpty()) {
                // 这里只取第一个结果做展示
                data.put("请求结果", item.getResponse().get(0).getBody());
            } else {
                data.put("请求结果", "");
            }
            createTable(data, i, tableColorMap.get(item.getName()));
            log.info("已生成表格：" + item.getName());
        }
        log.info("----------成功生成接口文档----------");
        log.info("一共生成 {} 个表格", itemList.size());
        return this;
    }

    /**
     * 获得接口名称，带排序
     *
     * @return {@link List}<{@link String}>
     */
    private List<String> getApiNames() {
        List<Item> itemList = postman.getItem();
        if (itemList == null || itemList.isEmpty()) {
            return Collections.emptyList();
        }
        List<String> result = new ArrayList<>(itemList.size());
        for (int i = 0; i < itemList.size(); i++) {
            Item item = itemList.get(i);
            // sortIndex = "1"
            String sortIndex = String.valueOf(i + 1);
            item.setSortIndex(sortIndex);
            addIndexName(result, item);
        }
        return result;
    }

    /**
     * 添加索引名称
     *
     * @param data 数据
     * @param item 项
     */
    private void addIndexName(List<String> data, Item item) {
        List<Item> itemList = item.getItem();
        // sortIndex = "1"
        String sortIndex = item.getSortIndex();
        // indexName = 序号 + "、" + name
        data.add(sortIndex + "、" + item.getName());
        if (itemList != null && !itemList.isEmpty()) {
            for (int i = 0; i < itemList.size(); i++) {
                Item childItem = itemList.get(i);
                int last = i + 1;
                // sortIndex = "1.1"
                childItem.setSortIndex(sortIndex + Constant.NAME_INDEX_FLAG + last);
                addIndexName(data, childItem);
            }
        }
    }

    /**
     * 获取所有包含请求信息的item，将不同folder下的item合并在一起
     * folder本身也是item，但不包含请求信息
     *
     * @return {@link List}<{@link Item}>
     */
    private List<Item> getItems() {
        List<Item> itemList = postman.getItem();
        if (itemList == null || itemList.isEmpty()) {
            return Collections.emptyList();
        }
        List<Item> result = new ArrayList<>(itemList.size());
        for (Item item : itemList) {
            addItem(result, item);
        }
        return result;
    }

    /**
     * 添加item
     *
     * @param data 源数据
     * @param item item
     */
    private void addItem(List<Item> data, Item item) {
        List<Item> itemList = item.getItem();
        if (itemList != null && !itemList.isEmpty()) {
            for (Item childItem : itemList) {
                addItem(data, childItem);
            }
        } else {
            data.add(item);
        }
    }

    /**
     * 匹配标签，有多个标签，只取第一个
     *
     * @param config  配置
     * @param apiName api名称
     */
    private void matchTag(Config config, String apiName) {
        Matcher matchers = Constant.TAG_REG_PATTERN.matcher(apiName);
        if (matchers.find()) {
            String tag = matchers.group();
            if (config.getAddTags().contains(tag)) {
                addList.add(apiName);
            } else if (config.getUpdateTags().contains(tag)) {
                updateList.add(apiName);
            } else if (config.getDeleteTags().contains(tag)) {
                deleteList.add(apiName);
            }
        }
    }

    /**
     * 匹配替换postman中的环境字段，只匹配自定义的环境
     *
     * @param str str
     * @return {@link String}
     */
    private String matchEnvironment(String str) {
        if (envMap.isEmpty() || StrUtil.isBlank(str)) {
            return str;
        }
        Matcher matchers = Constant.ENV_REG_PATTERN.matcher(str);
        while (matchers.find()) {
            String name = matchers.group();
            String value = envMap.get(name);
            if (value != null) {
                str = CharSequenceUtil.replace(str, name, value);
            }
        }
        return str;
    }

}
